package com.xqboss.common.core.redis.lock;

import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.exception.base.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author Trent
 * @date 2023/8/15
 */
@Slf4j
@Component
public class RedisLockUtils {

    /*** 分布式锁固定前缀 ***/
    private static final String REDIS_LOCK = "redis_lock";
    /*** 分布式锁过期时间 ***/
    private static final Integer EXPIRE_TIME = 60;
    /*** 获取锁等待时间 ***/
    private static final Integer WAIT_TIME = 5;

    @Autowired
    private RedissonClient redissonClient;

    public RedissonClient getRedissonClient() {
        return redissonClient;
    }

    /**
     * 加锁
     *
     * @param lockKey   加锁唯一标识
     * @param lockValue 释放锁唯一标识（建议使用线程ID作为value）
     */
    public void lock(String lockKey, String lockValue) {
        lock(lockKey, lockValue, EXPIRE_TIME);
    }

    /**
     * 加锁
     *
     * @param lockKey   加锁唯一标识
     * @param lockValue 释放锁唯一标识（建议使用线程ID作为value）
     * @param timeout   超时时间（单位：S）
     */
    public void lock(String lockKey, String lockValue, Integer timeout) {
        lock(lockKey, lockValue, timeout, WAIT_TIME);
    }


    public void lock(String lockKey, String lockValue, Integer timeout, int waitTime) {
        lock(lockKey,lockValue,timeout,waitTime,"访问人数过多，请稍后再试！");
    }

    public void lock(String lockKey, String lockValue, Integer timeout, int waitTime,String msg) {
        log.info("分布式锁: {} -> {}", lockKey, lockValue);
        Assert.isTrue(StringUtils.isNotBlank(lockKey), "lock redis locks   as null.");
        Assert.isTrue(StringUtils.isNotBlank(lockValue), "lock the redis release lock is identified as .");
        // ----- 尝试获取锁，当获取到锁，则直接返回，否则，循环尝试获取
        RLock lock = redissonClient.getLock(REDIS_LOCK + ":" + lockKey);
        try {
            boolean success = lock.tryLock(waitTime, timeout, TimeUnit.SECONDS);
            if(!success){
                log.error("lock redis try lock fail. key: {}, value: {}", lockKey, lockValue);
                throw new ServiceException(msg);
            }
        } catch (InterruptedException e) {
            log.error("lock redis try lock fail. key: {}, value: {}", lockKey, lockValue);
            throw new ServiceException(msg);
        }
        log.info("锁成功: {} -> {}", lockKey, lockValue);
    }

    /**
     * 释放锁
     *
     * @param lockKey   加锁唯一标识
     * @param lockValue 释放锁唯一标识（建议使用线程ID作为value）
     */
    public void unLock(String lockKey, String lockValue) {
        Assert.isTrue(StringUtils.isNotBlank(lockKey), "unLock locks are identified as null.");
        Assert.isTrue(StringUtils.isNotBlank(lockValue), "unLock the redis release lock is identified as null.");
        String tmpKey = REDIS_LOCK + ":" + lockKey;
        log.info("解除锁：{}", lockKey);
        RLock lock = redissonClient.getLock(tmpKey);
        lock.unlockAsync();
    }


    public void lock(String lockKey, Exec runnable) throws Exception{
        lock(lockKey, Thread.currentThread().getName());
        try{
            runnable.exec();
        }finally {
            unLock(lockKey, Thread.currentThread().getName());
        }
    }

    public interface Exec {
        void exec() throws Exception;
    }
}
